-- Copyright (c) 2015,2016 Lucky Byte, Inc.
--
-- Summary : 交易控制测试
--
-- Author  : xiaohu <xiaohu@lucky-byte.com>
-- Since   : 2016.7.8
--
-- History
--  * 2015.07.08 : 创建此文件
--

-- 检查交易起始/结束时间
local _check_time_range = function(trctl)
    local _now = core.date()
    _now.daynum = 0

    -- 检查交易起始时间
    if string.trimlen(trctl.time_begin) > 0 then
        local _time = core.date(trctl.time_begin)
        if _time.dayfrc > 0 and _now < _time then
            logger.error('当前时间[%s]早于交易起始时间[%s]，不允许交易.',
                _now:fmt('%H:%M:%S'), trctl.time_begin)
            return '09'
        end
    end
    -- 检查交易结束时间
    if string.trimlen(trctl.time_end) > 0 then
        local _time = core.date(trctl.time_end)
        if _time.dayfrc > 0 and _now > _time then
            logger.error('当前时间[%s]晚于交易结束时间[%s]，不允许交易.',
                _now:fmt('%H:%M:%S'), trctl.time_end)
            return '09'
        end
    end
    return '00'
end


-- 检查单笔交易额度
local _check_amt_per = function(trctl, tramt)
    if string.trimlen(trctl.min_amt_per) == 0 then
        logger.debug('交易控制中未配置单笔交易最小限额，不作检查...')
    else
        local _min_amt_per = tonumber(trctl.min_amt_per) or 0.0
        if _min_amt_per > 0.0 and tramt < _min_amt_per then
            logger.error('交易金额[%s]小于单笔交易最低金额[%s]，不允许交易.',
                tramt, _min_amt_per)
            return '08'
        end
    end
    if string.trimlen(trctl.max_amt_per) == 0 then
        logger.debug('交易控制中未配置单笔交易最大限额，不作检查...')
        return '00'
    else
        local _max_amt_per = tonumber(trctl.max_amt_per) or 0.0
        if _max_amt_per > 0.0 and tramt > _max_amt_per then
            logger.error('交易金额[%s]大于单笔交易最大金额[%s]，不允许交易.',
                tramt, _max_amt_per)
            return '06'
        end
    end
    return '00'
end


-- 检查分钟交易配额
local _check_minute_quota = function(trctl, tramt, merch, trcode)
    local _sql = [[
        select count(*)::int as count,
            case
            when chnl in (1,2,4) then
                sum(cast(regexp_replace(
                    coalesce(json_extract_path_text(o_pkt, '4'), '0'),
                    '[^0-9]+', '', 'g') as decimal)) / 100
            when chnl = 3 then
                sum(cast(regexp_replace(
                    coalesce(json_extract_path_text(i_pkt, 'txnAmt'), '0'),
                    '[^0-9]+', '', 'g') as decimal)) / 100
            end as amt
        from pay_trlist
        where
            sys_time >= CURRENT_TIMESTAMP - interval '1 minute' and
            sys_time <= CURRENT_TIMESTAMP and
            json_extract_path_text(trinfo, 'code') = ? and
            status = 0 and incoming = false and chnl = ? and
            case
            when chnl in (1,2,4) then
                json_extract_path_text(o_pkt, '39') = '00' and
                json_extract_path_text(o_pkt, '42') = ?
            when chnl = 3 then
                json_extract_path_text(o_pkt, 'respCode') = '00' and
                json_extract_path_text(o_pkt, 'merId') = ?
            end
        group by chnl
    ]]
    local _table = jdbc.select(_sql, trcode, merch.get('chnl'),
        merch.get('merno'), merch.get('merno'))
    if _table.count == 0 then
        return '00'
    end
    local _record = _table.get_record(0)

    -- 检查笔数限制
    if string.trimlen(trctl.max_cnt_minute) == 0 then
        logger.debug('交易控制中未配置分钟最大笔数限制，不作检查...')
    else
        local _max_cnt = tonumber(trctl.max_cnt_minute) or -1
        if _max_cnt < 0 then
            notify.warn('交易控制中分钟最大交易笔数配置[%s]有误，请检查.',
                trctl.max_cnt_minute)
            return '01'
        end
        -- 配置如果为 0 表示不限制
        if _max_cnt > 0 then
            local _count = _record.get('count') or 0
            if _count >= _max_cnt then
                logger.error(
                    '最近一分钟累计交易笔数[%s]已达到或超出交易限量[%s]，不允许继续交易.',
                    _count, _max_cnt)
                return '07'
            end
        end
    end

    -- 检查金额限制
    if string.trimlen(trctl.max_amt_minute) == 0 then
        logger.debug('没有配置分钟最大金额限制，不作检查...')
    else
        local _max_amt = tonumber(trctl.max_amt_minute) or -1
        if _max_amt < 0 then
            notify.warn('交易控制中分钟最大交易金额[%s]配置有误，请检查.',
                trctl.max_amt_minute)
            return '01'
        end
        -- 配置如果为 0 表示不限制
        if _max_amt > 0 then
            local _amt = _record.get('amt') or 0
            if _amt + tramt > _max_amt then
                logger.error(
                    '最近一分钟累计交易金额[%s]+当前交易金额[%s]已达到或超出交易限额[%s]，不允许继续交易.',
                    _amt, tramt, _max_amt)
                return '07'
            end
        end
    end
    return '00'
end


-- 检查日交易配额
local _check_day_quota = function(trctl, tramt, merch, trcode)
    local _sql = [[
        select count(*)::int as count,
            case
            when chnl in (1,2,4) then
                sum(cast(regexp_replace(
                    coalesce(json_extract_path_text(o_pkt, '4'), '0'),
                    '[^0-9]+', '', 'g') as decimal)) / 100
            when chnl = 3 then
                sum(cast(regexp_replace(
                    coalesce(json_extract_path_text(i_pkt, 'txnAmt'), '0'),
                    '[^0-9]+', '', 'g') as decimal)) / 100
            end as amt
        from pay_trlist
        where
            sys_time::date = CURRENT_DATE and
            json_extract_path_text(trinfo, 'code') = ? and
            status = 0 and incoming = false and chnl = ? and
            case
            when chnl in (1,2,4) then
                json_extract_path_text(o_pkt, '39') = '00' and
                json_extract_path_text(o_pkt, '42') = ?
            when chnl = 3 then
                json_extract_path_text(o_pkt, 'respCode') = '00' and
                json_extract_path_text(o_pkt, 'merId') = ?
            end
        group by chnl
    ]]
    local _table = jdbc.select(_sql, trcode, merch.get('chnl'),
        merch.get('merno'), merch.get('merno'))
    if _table.count == 0 then
        return '00'
    end
    local _record = _table.get_record(0)

    -- 检查笔数限制
    if string.trimlen(trctl.max_cnt_day) == 0 then
        logger.debug('交易控制中没有配置单日最大笔数限制，不作检查...')
    else
        local _max_cnt = tonumber(trctl.max_cnt_day) or -1
        if _max_cnt < 0 then
            notify.warn('交易控制中单日最大交易笔数[%s]配置有误，请检查...',
                trctl.max_cnt_day)
            return '01'
        end
        if _max_cnt > 0 then    -- 配置为 0 表示不限制
            local _count = _record.get('count') or 0
            if _count >= _max_cnt then
                logger.error(
                    '当日累计交易笔数[%s]已达到或超出交易限量笔数[%s]，不允许继续交易.',
                    _count, _max_cnt)
                return '07'
            end
        end
    end

    -- 检查金额限制
    if string.trimlen(trctl.max_amt_day) == 0 then
        logger.debug('交易控制中没有配置单日最大金额限制，不作检查...')
    else
        local _max_amt = tonumber(trctl.max_amt_day) or -1
        if _max_amt < 0 then
            notify.warn('交易控制中单日最大交易金额[%s]配置有误，请检查...',
                trctl.max_amt_day)
            return '01'
        end
        if _max_amt > 0 then    -- 配置为 0 表示不限制
            local _amt = _record.get('amt') or 0
            if _amt + tramt > _max_amt then
                logger.error(
                    '当日累计交易金额[%s]+当前交易金额[%s]已达到或超出交易限额[%s]，不允许继续交易.',
                    _amt, tramt, _max_amt)
                return '07'
            end
        end
    end
    return '00'
end


-- 返回系统响应码
--
local function _do_check(merch, trinfo, tramt, trctl)
    -- 检查交易权限
    if not trctl.trcodes then
        notify.error('交易控制配置错误[未配置交易权限]，请查看日志.')
        return '04'
    end
    if not trctl.trcodes[trinfo.code] then
        logger.error('不允许执行交易[%s][%s]，权限拒绝.', trinfo.code, trinfo.name)
        return '04'
    end

    logger.debug('继续检查交易[%s]的交易控制...', trinfo.name)

    -- 助农代付和消费代付采用同样的交易控制
    local _trcode_trctl = trinfo.code == 'tz1' and trctl.tz0 or trctl[trinfo.code]
    if not _trcode_trctl then
        logger.warn('未配置交易[%s][%s]的限制条件，不作检查...',
            trinfo.name, trinfo.code)
        return '00'
    end

    -- 交易起始/结束时间
    local _rescode = _check_time_range(_trcode_trctl)
    if _rescode ~= '00' then
        return _rescode
    end

    if not tramt then
        logger.warn('交易[%s]的金额为空，不能检查限额，请检查.', trinfo.name)
        return '00'
    end

    -- 检查单笔最低/最高限额
    local _rescode = _check_amt_per(_trcode_trctl, tramt)
    if _rescode ~= '00' then
        return _rescode
    end

    -- 检查分钟配额
    local _rescode = _check_minute_quota(_trcode_trctl, tramt, merch, trinfo.code)
    if _rescode ~= '00' then
        return _rescode
    end

    -- 检查日配额
    local _rescode = _check_day_quota(_trcode_trctl, tramt, merch, trinfo.code)
    if _rescode ~= '00' then
        return _rescode
    end
    return '00'
end


-- 检查商户的交易控制
--
-- args.merch:  商户记录
-- args.trinfo: 交易信息
-- args.tramt:  交易金额
--
local _check = function(args)
    if not args.merch then
        logger.error('参数错误，检查商户交易控制必须提供商户信息.')
        return '01'
    end

    -- 如果商户没有配置交易控制，则允许其进行任何交易
    local _trctl = args.merch.get('trctl')
    if not _trctl then
        logger.warn('商户[%s][%s]未配置交易控制，允许此商户的所有交易...',
            args.merch.get('merno'), args.merch.get('name'))
        return '00'
    end
    local _trctl = core.json.decode(_trctl)
    if not _trctl then
        logger.error('商户[%s]配置到交易控制[%s]格式无效.', merch.get('trctl'))
        return '01'
    end
    local _ret = _do_check(args.merch, args.trinfo, args.tramt, _trctl)
    if _ret ~= '00' then
        notify.warn('商户[%s]配置的交易控制拒绝当前的交易[%s]，请查看日志.',
            args.merch.get('merno'), args.trinfo.name)
    end
    return _ret
end


-- 外部接口
core = core or {}
core.trctl = {
    check = _check
}
